package com.wan.data.mysql.orm.meta;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.util.StrUtil;
import com.wan.data.mysql.exception.DBInitializeException;
import com.wan.data.mysql.orm.annotion.*;
import com.wan.data.mysql.orm.convert.Converter;
import com.wan.util.ClassUtil;
import com.wan.util.SafeUtil;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.*;

public class MetaUtil {

    private static final Map<Class<?>,TableMeta> tableMetaMap = new HashMap<>();

    public static TableMeta getTableMeta(Class<?> entity){
        return tableMetaMap.computeIfAbsent(entity,cls ->{
            Table table = cls.getAnnotation(Table.class);
            if(table == null){
                throw new DBInitializeException(entity.getName() + "实体类缺少Table注解!");
            }
            TableMeta tableMeta = new TableMeta(entity,table.databaseName(),table.tableName(),table.autoBegin());
            Map<String,IndexMeta> indexMetaMap = new HashMap<>();
            UnionIndex[] annotationsByType = cls.getAnnotationsByType(UnionIndex.class);
            for (UnionIndex unionIndex : annotationsByType) {
                if(unionIndex.columns().length < 2){
                    throw new DBInitializeException(entity.getName() + "联合索引必须指定多个字段");
                }
                for (String column : unionIndex.columns()) {
                    try {
                        Field field = cls.getDeclaredField(column);
                    } catch (NoSuchFieldException e) {
                        throw new DBInitializeException(e);
                    }
                }
                String indexName = StrUtil.isEmpty(unionIndex.name()) ? "more_idx_" + unionIndex.columns()[0] : unionIndex.name();
                IndexMeta indexMeta = new IndexMeta(indexName, unionIndex.type());
                indexMeta.setColumns(Arrays.asList(unionIndex.columns()));
                tableMeta.addIndexMeta(indexMeta);
                indexMetaMap.put(indexName,indexMeta);
            }
            List<Field> fields = ClassUtil.getFieldsNoStatic(cls);
            for (Field field : fields) {
                ColumnMeta columnMeta = getColumnMeta(field);
                if(columnMeta != null){
                    tableMeta.addColumnMeta(columnMeta);
                    //单字段索引
                    Index index = field.getAnnotation(Index.class);
                    if(index != null){
                        IndexMeta indexMeta = new IndexMeta(
                                StrUtil.isEmpty(index.value()) ? "idx_" + columnMeta.getColumnName() : index.value(),index.type());
                        indexMeta.addColumnMeta(columnMeta);
                        tableMeta.addIndexMeta(indexMeta);
                    }
                    //联合索引
                    for (IndexMeta indexMeta : indexMetaMap.values()) {
                        if(indexMeta.getColumns().contains(field.getName())){
                            indexMeta.addColumnMeta(columnMeta);
                        }
                    }
                }
            }
            if(tableMeta.getPkColumn() == null){
                throw new DBInitializeException(entity.getName() + "实体类缺少主键");
            }
            return tableMeta;
        });
    }

    public static ColumnMeta getColumnMeta(Field field){
        Class<?> type = field.getType();
        //判断map类型是否是线程安全
        if(Map.class.isAssignableFrom(type)){
            Class<? extends Map<?,?>> mapType = (Class<? extends Map<?, ?>>) type;
            if(!SafeUtil.isSafeMap(mapType)){
                throw new DBInitializeException("实体属性Map类型必须是线程安全的! field:" + field);
            }
        }
        //判断集合是否是线程安全
        if(Collection.class.isAssignableFrom(type)){
            Class<? extends Collection<?>> collType = (Class<? extends Collection<?>>) type;
            if(!SafeUtil.isSafeCollection(collType)){
                throw new DBInitializeException("实体属性集合类型必须是线程安全的! field:" + field);
            }
        }
        Column column = field.getAnnotation(Column.class);
        if(column == null){
            return null;
        }
        boolean isPK = field.getAnnotation(PK.class) != null;
        boolean noNull = isPK || column.notNull();
        ColumnMeta columnMeta = new ColumnMeta(field,column.name(),column.length(),
                column.scale(),noNull,column.comment(),
                column.converter(),column.autoIncrement());
        Converter converter = Converter.get(columnMeta);
        converter.tryCheck(columnMeta);
        columnMeta.setColumnType(converter.dbType(columnMeta.getLength(), columnMeta.getScale()));
        if(isPK){
            columnMeta.setPk();
        }
        return columnMeta;
    }



}
